#include <cstring>
#include <sys/stat.h>
#include "securec.h"
#include "dlna_play_controller.h"
#include "cast_engine_errors.h"
#include "cast_engine_log.h"
#include "utils.h"
#include "dmcApi.h"
#include <nlohmann/json.hpp>

namespace OHOS {
namespace CastEngine {
namespace CastEngineService {
DEFINE_CAST_ENGINE_LABEL("Cast-Player-Controller-Dlna");

using namespace std;
using json = nlohmann::json;

sptr<IStreamPlayerListenerImpl> DlnaPlayController::playerListener_;
std::condition_variable DlnaPlayController::fdCondition_;
std::mutex DlnaPlayController::fdMutex_;
MediaInfo DlnaPlayController::mediaInfo_;
int DlnaPlayController::fd_ = 0;
bool DlnaPlayController::autoUpdate_ = false;
std::weak_ptr<ICastStreamManagerClient> DlnaPlayController::dlnaCallback_;
namespace {
constexpr int ALLOW_ERROR = 2;
} // namespace
uint32_t DlnaPlayController::DlnaGetLocalFd(char *uri)
{
    CLOGI("DlnaGetLocalFd in");
    if (uri == nullptr) {
        CLOGE("DlnaGetLocalFd empty uri");
        return 0;
    }
    std::unique_lock<std::mutex> lck(fdMutex_);
    CLOGI("DlnaGetLocalFd uri:%{public}s, fd:%{public}d", uri, fd_);
    return fd_;
}

DlnaPlayController::DlnaPlayController(std::shared_ptr<ICastStreamManagerClient> callback,
    std::shared_ptr<CastLocalFileChannelServer> fileChannel, uint32_t deviceId)
    : callback_(callback), fileChannelServer_(fileChannel)
{
    CLOGI("DlnaPlayController in deviceId is %{public}d", deviceId);
    dlnaDevId_ = deviceId;
    dlnaCallback_ = callback;
    //DLNA_INT32 result = DlnaApiRegisterDmcCallback(DLNA_DMC_CBK_GET_FD, (void *)DlnaPlayController::DlnaGetLocalFd);
    // if (result != DlnaDmc::DMC_SUCCESS) {
    //     CLOGE("dlna register get local fd callback error");
    // }
}

DlnaPlayController::~DlnaPlayController()
{
    CLOGI("~DlnaPlayController in");
}

void DlnaPlayController::SetSessionCallbackForRelease(const std::function<void(void)>& callback)
{
    CLOGI("SetSessionCallbackForRelease in");
    std::lock_guard<std::mutex> lock(sessionCallbackMutex_);
    sessionCallback_ = callback;
    DlnaManager::GetInstance().SetDlnaCallback(std::make_shared<DlnaCallbackImpl>(shared_from_this()));
}

int32_t DlnaPlayController::RegisterListener(sptr<IStreamPlayerListenerImpl> listener)
{
    CLOGI("RegisterListener in");
    if (!listener) {
        CLOGE("listener is null");
        return CAST_ENGINE_ERROR;
    }
    std::shared_ptr<ICastStreamManagerClient> targetCallback = callback_.lock();
    if (!targetCallback) {
        CLOGE("ICastStreamManagerClient is null");
        return CAST_ENGINE_ERROR;
    }
    if (!targetCallback->RegisterListener(listener)) {
        CLOGE("RegisterListener failed");
        return CAST_ENGINE_ERROR;
    }
    std::lock_guard<std::mutex> lock(listenerMutex_);
    playerListener_ = listener;
    return CAST_ENGINE_SUCCESS;
}

int32_t DlnaPlayController::UnregisterListener()
{
    CLOGI("UnregisterListener in");
    if (timer_ != nullptr) {
        timer_->Stop();
    }
    {
        std::lock_guard<std::mutex> lock(listenerMutex_);
        playerListener_ = nullptr;
    }

    std::shared_ptr<ICastStreamManagerClient> targetCallback = callback_.lock();
    if (!targetCallback) {
        CLOGE("ICastStreamManagerClient is null");
        return CAST_ENGINE_ERROR;
    }
    if (!targetCallback->UnregisterListener()) {
        CLOGE("UnregisterListener failed");
        return CAST_ENGINE_ERROR;
    }

    return CAST_ENGINE_SUCCESS;
}

int32_t DlnaPlayController::SetSurface(sptr<IBufferProducer> producer)
{
    CLOGE("Don't support SetSurface");
    static_cast<void>(producer);
    return CAST_ENGINE_ERROR;
}

int32_t DlnaPlayController::Load(const MediaInfo &mediaInfo)
{
    CLOGE("Don't support load");
    if (mediaInfo.mediaUrl.empty() || mediaInfo.mediaUrl == "http:") {
        CLOGE("Incorrect media URL");
        return CAST_ENGINE_SUCCESS;
    } else {
        Play(mediaInfo);
    }
    return CAST_ENGINE_SUCCESS;
}

bool DlnaPlayController::AutoUpdateCurInfo()
{
    AutoUpdatePlayerState();
    autoUpdate_ = !autoUpdate_;
    if (autoUpdate_) {
        AutoUpdateCurPosition();
        AutoUpdateVolume();
    }
    return true;
}

bool DlnaPlayController::AutoUpdateCurPosition()
{
    int duration = 0;
    int current = 0;

    if (GetTimeInfo(&current, &duration) != CAST_ENGINE_SUCCESS) {
        CLOGE("AutoUpdateCurPosition: GetTimeInfo failed");
        return false;
    }
    int realTime = pucRelTime_;
    if (duration == 0 && realTime == 0) {
        CLOGE("duration is 0, it is invalid, realTime is 0.");
        return false;
    }
    currentPosition_ = current * DLNA_MIL_SECOND; // ms
    durationPosition_ = duration * DLNA_MIL_SECOND;
    std::lock_guard<std::mutex> lock(listenerMutex_);
    string playOnline_url = mediaInfo_.mediaUrl;
    CLOGI("playOnline_url is %{public}s", Utils::Mask(playOnline_url).c_str());
    std::string CacheUrl = pucTrackURI_;
    CLOGI("pucTrackURI is %{public}s", Utils::Mask(pucTrackURI_).c_str());
    /* 如果是在线视频，且获取的缓存URL和播放URL不一致，则认为是抢占，停止播放 */
    if (Utils::StartWith(playOnline_url, "http") && playOnline_url != CacheUrl) {
        CLOGI("URL is different.");
    }
    if (!playerListener_) {
        CLOGE("playerListener is nullptr");
        return false;
    }
    if (firstGetPosition_) {
        firstGetPosition_ = false;
        playerListener_->OnStateChanged(playerStates_, false);
    }
    CLOGI("needSeekPosition_ is %{public}d, currentPosition_ %{public}d, durationPosition_ %{public}d",
        needSeekPosition_, currentPosition_, durationPosition_);
    if (durationPosition_ == 0) {
        durationPosition_ = static_cast<int>(mediaInfo_.duration);
    }
    playerListener_->OnPositionChanged(currentPosition_, CAST_STREAM_INT_IGNORE, durationPosition_);
    return true;
}

bool DlnaPlayController::AutoUpdateVolume()
{
    int maxVolume;
    int volume;
    if (DlnaGetVolume(volume, maxVolume) != CAST_ENGINE_SUCCESS) {
        CLOGW("Auto update volume failed");
        return false;
    }
    if (volume_ == volume) {
        return true;
    }
    volume_  = volume;
    CLOGI("AutoUpdateCurVolume:%{public}d", volume_);
    std::lock_guard<std::mutex> lock(listenerMutex_);
    if (!playerListener_) {
        CLOGE("playerListener is nullptr");
        return false;
    }
    playerListener_->OnVolumeChanged(volume_, maxVolume);
    return true;
}

bool DlnaPlayController::AutoUpdatePlayerState()
{
    CLOGI("DlnaPlayController::AutoUpdatePlayerState()");
    PlayerStates states;
    if (DlnaGetPlayerStatus(states) != CAST_ENGINE_SUCCESS) {
        CLOGW("Auto update player state failed");
        return false;
    }
    if (playerStates_ == states) {
        return true;
    }
    if (states == PlayerStates::PLAYER_STOPPED && playerStates_ == PlayerStates::PLAYER_IDLE) {
        CLOGI("Not connected do not process stop message");
        return true;
    } else if (firstGetState_ && states == PlayerStates::PLAYER_STARTED) {
        firstGetState_ = false;
        CLOGI("mediaInfo_.startPosition is %{public}d, mediaInfo_.duration is %{public}d",
            mediaInfo_.startPosition, mediaInfo_.duration);
        if (mediaInfo_.startPosition > 0) {
            CLOGI("The first playback requires seek");
            Seek(mediaInfo_.startPosition);
        }
    }
    stoppedCnt_ = 0;
    playerStates_ = states;
    CLOGI("AutoUpdateCurStatus:%{public}d", playerStates_.load());
    std::lock_guard<std::mutex> lock(listenerMutex_);
    if (!playerListener_) {
        CLOGE("playerListener is nullptr");
        return false;
    }
    CLOGI("currentPosition_ is %{public}d, durationPosition_ %{public}d", currentPosition_, durationPosition_);
    if (states == PlayerStates::PLAYER_STOPPED && currentPosition_ <= durationPosition_ &&
        ((durationPosition_ - currentPosition_) / DLNA_MIL_SECOND) <= ALLOW_ERROR) {
        playerListener_->OnEndOfStream(false);
        playerListener_->OnSeekDone(mediaInfo_.duration);
        playerListener_->OnStateChanged(PlayerStates::PLAYER_PLAYBACK_COMPLETE, false);
    } else if (states == PlayerStates::PLAYER_STOPPED && currentPosition_ <= durationPosition_ &&
        ((durationPosition_ - currentPosition_) / DLNA_MIL_SECOND) > ALLOW_ERROR) {
        DisconnectSession(DlnaManager::GetInstance().GetDeviceUuid(dlnaDevId_));
    } else {
        playerListener_->OnStateChanged(playerStates_, false);
    }
    return true;
}

int32_t DlnaPlayController::Play(const MediaInfo &mediaInfo)
{
    CLOGI("Start in");
    std::string jsonMediaInfoStr = MediaInfoToJson(mediaInfo);
    SendCastMessage(Message(MessageId::DLNA_MSG_PLAY, jsonMediaInfoStr));
    CLOGI("Start out");
    return CAST_ENGINE_SUCCESS;
}

std::string DlnaPlayController::MediaInfoToJson(const MediaInfo &mediaInfo)
{
    nlohmann::json jsonMediaInfo;
    jsonMediaInfo["mediaId"] = mediaInfo.mediaId;
    jsonMediaInfo["mediaName"] = mediaInfo.mediaName;
    jsonMediaInfo["mediaUrl"] = mediaInfo.mediaUrl;
    jsonMediaInfo["mediaType"] = mediaInfo.mediaType;
    jsonMediaInfo["mediaSize"] = mediaInfo.mediaSize;
    jsonMediaInfo["startPosition"] = mediaInfo.startPosition;
    jsonMediaInfo["duration"] = mediaInfo.duration;
    jsonMediaInfo["closingCreditsPosition"] = mediaInfo.closingCreditsPosition;
    jsonMediaInfo["albumCoverUrl"] = mediaInfo.albumCoverUrl;
    jsonMediaInfo["mediaArtist"] = mediaInfo.mediaArtist;
    jsonMediaInfo["lrcUrl"] = mediaInfo.lrcUrl;
    jsonMediaInfo["lrcContent"] = mediaInfo.lrcContent;
    jsonMediaInfo["appIconUrl"] = mediaInfo.appIconUrl;
    jsonMediaInfo["appName"] = mediaInfo.appName;
    jsonMediaInfo["drmType"] = mediaInfo.drmType;
    std::string dump = jsonMediaInfo.dump();
    CLOGI("MediaInfoToJson - out - JSON: %{public}s", dump.c_str());
    return dump;
}

MediaInfo DlnaPlayController::JsonToMediaInfo(const nlohmann::json& jsonMediaInfo, MediaInfo& mediaInfo)
{
    CLOGI("JsonToMediaInfo");
    if (jsonMediaInfo.find("mediaId") != jsonMediaInfo.end()) {
        mediaInfo.mediaId = jsonMediaInfo["mediaId"];
    }
    if (jsonMediaInfo.find("mediaName") != jsonMediaInfo.end()) {
        mediaInfo.mediaName = jsonMediaInfo["mediaName"];
    }
    if (jsonMediaInfo.find("mediaUrl") != jsonMediaInfo.end()) {
        mediaInfo.mediaUrl = jsonMediaInfo["mediaUrl"];
    }
    if (jsonMediaInfo.find("mediaType") != jsonMediaInfo.end()) {
        mediaInfo.mediaType = jsonMediaInfo["mediaType"];
    }
    if (jsonMediaInfo.find("mediaSize") != jsonMediaInfo.end()) {
        mediaInfo.mediaSize = jsonMediaInfo["mediaSize"];
    }
    if (jsonMediaInfo.find("startPosition") != jsonMediaInfo.end()) {
        mediaInfo.startPosition = jsonMediaInfo["startPosition"];
    }
    if (jsonMediaInfo.find("duration") != jsonMediaInfo.end()) {
        mediaInfo.duration = jsonMediaInfo["duration"];
    }
    if (jsonMediaInfo.find("closingCreditsPosition") != jsonMediaInfo.end()) {
        mediaInfo.closingCreditsPosition = jsonMediaInfo["closingCreditsPosition"];
    }
    if (jsonMediaInfo.find("albumCoverUrl") != jsonMediaInfo.end()) {
        mediaInfo.albumCoverUrl = jsonMediaInfo["albumCoverUrl"];
    }
    if (jsonMediaInfo.find("albumTitle") != jsonMediaInfo.end()) {
        mediaInfo.albumTitle = jsonMediaInfo["albumTitle"];
    }
    if (jsonMediaInfo.find("mediaArtist") != jsonMediaInfo.end()) {
        mediaInfo.mediaArtist = jsonMediaInfo["mediaArtist"];
    }
    if (jsonMediaInfo.find("lrcUrl") != jsonMediaInfo.end()) {
        mediaInfo.lrcUrl = jsonMediaInfo["lrcUrl"];
    }
    if (jsonMediaInfo.find("lrcContent") != jsonMediaInfo.end()) {
        mediaInfo.lrcContent = jsonMediaInfo["lrcContent"];
    }
    if (jsonMediaInfo.find("appIconUrl") != jsonMediaInfo.end()) {
        mediaInfo.appIconUrl = jsonMediaInfo["appIconUrl"];
    }
    if (jsonMediaInfo.find("appName") != jsonMediaInfo.end()) {
        mediaInfo.appName = jsonMediaInfo["appName"];
    }
    if (jsonMediaInfo.find("drmType") != jsonMediaInfo.end()) {
        mediaInfo.drmType = jsonMediaInfo["drmType"];
    }
    return mediaInfo;
}

int32_t DlnaPlayController::PlayInner(const MediaInfo &mediaInfo)
{
    CLOGI("PlayInner in");
    CLOGI("  mediaId   = %{public}s", mediaInfo.mediaId.c_str());
    CLOGI("  mediaName = %{public}s", mediaInfo.mediaName.c_str());
    CLOGI("  mediaUrl  = %{public}s", mediaInfo.mediaUrl.c_str());
    CLOGI("  mediaType = %{public}s", mediaInfo.mediaType.c_str());
    CLOGI("  duration  = %{public}d", mediaInfo.duration);
    // 构造元数据
    DlnaUpnpMediaType mediaTypeEnum;
    if (!GetMediaTypeEnumFromString(mediaInfo.mediaType, mediaTypeEnum)) {
        NotifyPlayerError(ERROR_CODE_INVALID_PARAM, "unsupported media type");
        return CAST_ENGINE_ERROR;
    }
    std::string meta = DlnaGetMetaData(mediaTypeEnum, mediaInfo.mediaUrl, mediaInfo);

    // 1) 设置 URI + 元数据
    auto r1 = dmcApi_.SetAvTransportURI(dlnaDevId_, 0,
                                       mediaInfo.mediaUrl, meta);
    if (r1 != DlnaDmc::DMC_SUCCESS) {
        NotifyPlayerError(ERROR_CODE_UNSPECIFIED, "SetAvTransportURI failed");
        return CAST_ENGINE_ERROR;
    }

    // 2) 开始播放
    std::string speed = "1";
    auto r2 = dmcApi_.Play(dlnaDevId_, 0, speed);
    if (r2 != DlnaDmc::DMC_SUCCESS) {
        NotifyPlayerError(ERROR_CODE_UNSPECIFIED, "Play failed");
        return CAST_ENGINE_ERROR;
    }

    // **立即进入 BUFFERING，等第一次轮询完成后切实际 STARTED**
    playerStates_.store(PlayerStates::PLAYER_BUFFERING);
    firstBuffering_ = true;

    // 3) 启动定时器轮询
    stoppedCnt_ = 0;
    timer_->Start([this]() {
        AutoUpdateCurInfo();
        // 第一次轮询完成以后，从 BUFFERING 切到 STARTED 并通知上层
        if (firstBuffering_ && playerStates_.load() == PlayerStates::PLAYER_BUFFERING) {
            firstBuffering_ = false;
            playerStates_.store(PlayerStates::PLAYER_STARTED);
            CLOGI("Play actually started -> state=STARTED");
            std::lock_guard<std::mutex> lock(listenerMutex_);
            if (playerListener_) {
                playerListener_->OnStateChanged(PlayerStates::PLAYER_STARTED, false);
            }
        }
    }, DLNA_POSITION_UPDATE_INTERVAL);

    // 4) 通知上层已切换媒体
    std::lock_guard<std::mutex> lock(listenerMutex_);
    if (playerListener_) {
        playerListener_->OnMediaItemChanged(mediaInfo);
    }
    DlnaManager::GetInstance().SetPlaybackStatus(DlnaManager::PLAYING);
    return CAST_ENGINE_SUCCESS;
}

int32_t DlnaPlayController::Play(int index)
{
    CLOGE("Don't support Play index remotely");
    static_cast<void>(index);
    return CAST_ENGINE_ERROR;
}

int32_t DlnaPlayController::Play()
{
    CLOGI("Play in");
    std::string speed = SpeedToString(currentSpeed_);
    auto ret = dmcApi_.Play(dlnaDevId_, 0, speed);
    if (ret != DlnaDmc::DMC_SUCCESS) {
        CLOGE("DMC Play failed: %{public}d", ret);
        NotifyPlayerError(ret, "Play failed");
        return CAST_ENGINE_ERROR;
    }
    if (!timer_) {
        CLOGE("timer_ is nullptr");
    } else {
        stoppedCnt_ = 0;
    }
    return CAST_ENGINE_SUCCESS;
}

int32_t DlnaPlayController::Pause()
{
    CLOGI("Pause in");
    SendCastMessage(Message(MessageId::DLNA_MSG_PAUSE));
    return CAST_ENGINE_SUCCESS;
}

int32_t DlnaPlayController::PauseInner()
{
    CLOGI("PauseInner in");
    auto ret = dmcApi_.Pause(dlnaDevId_, 0);
    if (ret != DlnaDmc::DMC_SUCCESS) {
        NotifyPlayerError(ERROR_CODE_UNSPECIFIED, "Pause failed");
        return CAST_ENGINE_ERROR;
    }
    playerStates_.store(PlayerStates::PLAYER_PAUSED);
    return CAST_ENGINE_SUCCESS;
}

int32_t DlnaPlayController::Stop()
{
    CLOGI("Stop in");
    SendCastMessage(Message(MessageId::DLNA_MSG_STOP));
    CLOGI("Stop out");
    return CAST_ENGINE_SUCCESS;
}

int32_t DlnaPlayController::StopInner()
{
    CLOGI("StopInner in");
    timer_->Stop();
    auto ret = dmcApi_.Stop(dlnaDevId_, 0);
    if (ret != DlnaDmc::DMC_SUCCESS) {
        NotifyPlayerError(ERROR_CODE_UNSPECIFIED, "Stop failed");
    }
    playerStates_.store(PlayerStates::PLAYER_STOPPED);
    DlnaManager::GetInstance().SetPlaybackStatus(DlnaManager::STOPPED);
    return CAST_ENGINE_SUCCESS;
}

sptr<IStreamPlayerListenerImpl> DlnaPlayController::PlayerListenerGetter()
{
    CLOGI("PlayerListenerGetter in");
    std::lock_guard<std::mutex> lock(listenerMutex_);
    return playerListener_;
}

int32_t DlnaPlayController::Next()
{
    CLOGI("Next in");
    std::lock_guard<std::mutex> lock(listenerMutex_);
    if (!playerListener_) {
        CLOGI("Player listener is null");
        return CAST_ENGINE_ERROR;
    }
    playerListener_->OnNextRequest();
    return CAST_ENGINE_SUCCESS;
}

int32_t DlnaPlayController::Previous()
{
    CLOGI("Previous in");
    std::lock_guard<std::mutex> lock(listenerMutex_);
    if (!playerListener_) {
        CLOGI("Player listener is null");
        return CAST_ENGINE_ERROR;
    }
    playerListener_->OnPreviousRequest();
    return CAST_ENGINE_SUCCESS;
}

int32_t DlnaPlayController::Seek(int position)
{
    CLOGI("Seek in position:%{public}d", position);
    if (position < 0) {
        NotifyPlayerError(ERROR_CODE_INVALID_PARAM, "position < 0");
        return ERR_INVALID_PARAM;
    }
    int sec = position / DLNA_MIL_SECOND;
    std::string relTime = DlnaSeek(sec);
    auto ret = dmcApi_.Seek(dlnaDevId_, 0,
                            DlnaDmc::DMC_SEEK_MODE_REL_TIME,
                            relTime);
    if (ret != DlnaDmc::DMC_SUCCESS) {
        NotifyPlayerError(ERROR_CODE_UNSPECIFIED, "Seek failed");
        return CAST_ENGINE_ERROR;
    }
    // Seek 后状态保持原有 PLAYED/PAUSED，不自动切换
    std::lock_guard<std::mutex> lock(listenerMutex_);
    if (playerListener_) {
        playerListener_->OnSeekDone(position);
    }
    return CAST_ENGINE_SUCCESS;
}

string DlnaPlayController::DlnaSeek(int position)
{
    int second = position % DLNA_TIME_DIVIDER;
    int minute = (position / DLNA_TIME_DIVIDER) % DLNA_TIME_DIVIDER;
    int hour = position / DLNA_TIME_DIVIDER / DLNA_TIME_DIVIDER;
    string pos;
    if (hour < DECIMAL_10) {
        pos.append("0");
    }
    pos.append(to_string(hour));
    pos.append(":");
    if (minute < DECIMAL_10) {
        pos.append("0");
    }
    pos.append(to_string(minute));
    pos.append(":");
    if (second < DECIMAL_10) {
        pos.append("0");
    }
    pos.append(to_string(second));
    return pos;
}

int32_t DlnaPlayController::FastForward(int delta)
{
    CLOGI("FastForward in.");
    int position = 0;
    if (GetTimeInfo(&position, nullptr) != CAST_ENGINE_SUCCESS) {
        CLOGE("AutoUpdateCurPosition: GetTimeInfo failed");
        return CAST_ENGINE_ERROR;
    }
    CLOGI("position is %{public}d, delta %{public}d", position, delta);
    position = position * DLNA_MIL_SECOND + delta;
    if (position >= 0 && position < static_cast<int>(durationPosition_)) {
        Seek(position);
    } else if (position >= static_cast<int>(durationPosition_)) {
        Seek(static_cast<int>(durationPosition_) - ALLOW_ERROR);
    }
    return CAST_ENGINE_SUCCESS;
}

int32_t DlnaPlayController::FastRewind(int delta)
{
    CLOGI("FastRewind in.");
    int position = 0;
    if (GetTimeInfo(&position, nullptr) != CAST_ENGINE_SUCCESS) {
        CLOGE("AutoUpdateCurPosition: GetTimeInfo failed");
        return CAST_ENGINE_ERROR;
    }
    CLOGI("position is %{public}d, delta %{public}d", position, delta);
    position = position * DLNA_MIL_SECOND - delta;
    if (position >= 0 && position < static_cast<int>(durationPosition_)) {
        Seek(position);
    } else if (position < 0) {
        Seek(0);
    }
    return CAST_ENGINE_SUCCESS;
}

int32_t DlnaPlayController::SetVolume(int volume)
{
    CLOGI("SetVolume in volume:%{public}d", volume);
    auto ret = dmcApi_.SetVolume(dlnaDevId_, 0,
                                 DlnaDmc::DMC_CHANNEL_TYPE_MASTER,
                                 static_cast<DlnaDmc::dmc_uint32>(volume));
    if (ret != DlnaDmc::DMC_SUCCESS) {
        NotifyPlayerError(ERROR_CODE_UNSPECIFIED, "SetVolume failed");
        return CAST_ENGINE_ERROR;
    }
    return CAST_ENGINE_SUCCESS;
}

int32_t DlnaPlayController::SetMute(bool mute)
{
    CLOGI("SetMute in: %{public}d", mute);
    auto ret = dmcApi_.SetMute(dlnaDevId_, 0,
                               DlnaDmc::DMC_CHANNEL_TYPE_MASTER,
                               mute);
    if (ret != DlnaDmc::DMC_SUCCESS) {
        NotifyPlayerError(ERROR_CODE_UNSPECIFIED, "SetMute failed");
        return CAST_ENGINE_ERROR;
    }
    if (auto cb = callback_.lock()) {
        cb->NotifyPeerSetMute(mute);
    }
    return CAST_ENGINE_SUCCESS;
}

int32_t DlnaPlayController::SetLoopMode(const LoopMode mode)
{
    CLOGI("Don't support set loop mode.");
    auto loopMode = static_cast<int>(mode);
    CLOGI("SetLoopMode in mode:%{public}d", loopMode);
    std::shared_ptr<ICastStreamManagerClient> targetCallback = callback_.lock();
    if (!targetCallback) {
        CLOGE("ICastStreamManagerClient is null");
        return CAST_ENGINE_ERROR;
    }
    if (!targetCallback->NotifyPeerSetRepeatMode(loopMode)) {
        CLOGE("NotifyPeerSetRepeatMode failed");
        return CAST_ENGINE_ERROR;
    }
    return CAST_ENGINE_SUCCESS;
}

int32_t DlnaPlayController::SetSpeed(PlaybackSpeed speed)
{
    currentPosition_ = 1;
    volume_ = 1;

    CLOGI("SetSpeed in speed=%{public}d", speed);
    // new DMC API 直接传 string
    string s = SpeedToString(speed);
    auto ret = dmcApi_.Play(dlnaDevId_, 0, s);
    if (ret != DlnaDmc::DMC_SUCCESS) {
        CLOGE("Dlna set speed failed: %{public}d", ret);
        return CAST_ENGINE_ERROR;
    }
    return CAST_ENGINE_SUCCESS;
}
std::string DlnaPlayController::SpeedToString(PlaybackSpeed speed)
{
    auto it = speedMap.find(speed);
    if (it == speedMap.end()) {
        return "1";  // 默认正常速度
    }
    return it->second;
}

int32_t DlnaPlayController::DlnaGetPlayerStatus(PlayerStates &dlnaPlayerStates)
{
    CLOGI("DlnaGetPlayerStatus in");
    DlnaDmc::DmcTransportState state;
    DlnaDmc::DmcTransportStatus status;
    std::string playSpeed;

    auto ret = dmcApi_.GetTransportInfo(dlnaDevId_, 0, state, status, playSpeed);
    if (ret != DlnaDmc::DMC_SUCCESS || status != DlnaDmc::DMC_TRANSPORT_OK) {
        return CAST_ENGINE_ERROR;
    }

    switch (state) {
        case DlnaDmc::DMC_TRANSPORT_STOPPED:
            dlnaPlayerStates = PlayerStates::PLAYER_STOPPED;
            break;

        case DlnaDmc::DMC_TRANSPORT_PLAYING:
            dlnaPlayerStates = PlayerStates::PLAYER_STARTED;
            break;

        case DlnaDmc::DMC_TRANSPORT_PAUSED_PLAYBACK:
            dlnaPlayerStates = PlayerStates::PLAYER_PAUSED;
            break;

        case DlnaDmc::DMC_TRANSPORT_PAUSED_RECODING:
            dlnaPlayerStates = PlayerStates::PLAYER_STOPPED;
            break;

        case DlnaDmc::DMC_TRANSPORT_NO_MEDIA_PRESENT:
            dlnaPlayerStates = PlayerStates::PLAYER_IDLE;
            break;

        case DlnaDmc::DMC_TRANSPORT_TRANSITIONING:
            // no nothing.
        default:
            break;
    };
    CLOGI("DlnaGetPlayerStatus -> %{public}d", dlnaPlayerStates);
    return CAST_ENGINE_SUCCESS;
}

int32_t DlnaPlayController::GetPlayerStatus(PlayerStates &playerStates)
{
    CLOGI("AvSessionGetPlayerStatus in");
    playerStates = playerStates_;
    CLOGI("AvSessionGetPlayerStatus out, AvSessionGetPlayerStatus is %{public}d", playerStates);
    return CAST_ENGINE_SUCCESS;
}

int32_t DlnaPlayController::GetTimeInfo(int* position, int* duration)
{
    DlnaDmc::DmrTrackPosition tp{};
    auto ret = dmcApi_.GetPositionInfo(dlnaDevId_, 0, tp);
    if (ret != DlnaDmc::DMC_SUCCESS) {
        CLOGE("GetPositionInfo failed: %d", ret);
        return CAST_ENGINE_ERROR;
    }
    pucTrackURI_ = tp.trackUri;
    CLOGI("pucRelTime is %{public}s", tp.relTime.c_str());
    pucRelTime_ = TimeStringToInt(tp.relTime);

    if (duration != nullptr) {
        *duration = TimeStringToInt(tp.trackDur);
    }
    CLOGI("pucTrackDur is %{public}s", tp.trackDur.c_str());

    if (position != nullptr) {
        *position = TimeStringToInt(tp.absTime);
    }
    CLOGI("pucAbsTime is %{public}s", tp.absTime.c_str());

    return CAST_ENGINE_SUCCESS;
}

int32_t DlnaPlayController::GetPosition(int &position)
{
    CLOGI("AVSessionGetPosition in");
    position = currentPosition_;
    CLOGI("AVSessionGetPosition out, position is %{public}d", position);
    return CAST_ENGINE_SUCCESS;
}

int32_t DlnaPlayController::GetDuration(int &duration)
{
    CLOGI("GetDuration in");
    duration = durationPosition_;
    CLOGI("GetDuration out, GetDuration is %{public}d", duration);
    return CAST_ENGINE_SUCCESS;
}

int32_t DlnaPlayController::TimeStringToInt(const std::string& timeString)
{
    std::vector<std::string> timeVector;
    Utils::SplitString(timeString, timeVector, ":");
    int totalTime = 0;
    for (auto item : timeVector) {
        std::vector<std::string> subVector;
        Utils::SplitString(item, subVector, ".");
        if (static_cast<int>(subVector.size()) == subVectorSize) {
            totalTime = totalTime * DLNA_TIME_DIVIDER + Utils::StringToInt(subVector[0]);
        } else {
            totalTime = totalTime * DLNA_TIME_DIVIDER + Utils::StringToInt(item);
        }
    }
    return totalTime;
}

int32_t DlnaPlayController::DlnaGetVolume(int &dlnaVolume, int &maxVolume)
{
    CLOGI("DlnaGetVolume in");
    DlnaDmc::dmc_uint32 cur{0};
    auto ret = dmcApi_.GetVolume(dlnaDevId_, 0,
                                 DlnaDmc::DMC_CHANNEL_TYPE_MASTER,
                                 cur);
    if (ret != DlnaDmc::DMC_SUCCESS) {
        return CAST_ENGINE_ERROR;
    }
    dlnaVolume = static_cast<int>(cur);
    maxVolume = CAST_STREAM_FULL_VOLUME;
    return CAST_ENGINE_SUCCESS;
}

int32_t DlnaPlayController::GetVolume(int &volume, int &maxVolume)
{
    CLOGI("AVSessionGetVolume in");
    volume = volume_;
    CLOGI("AVSessionGetVolume out, AVSessionGetVolume is %{public}d", volume);
    return CAST_ENGINE_SUCCESS;
}

int32_t DlnaPlayController::GetMute(bool &mute)
{
    CLOGI("GetMute in");
    std::shared_ptr<ICastStreamManagerClient> targetCallback = callback_.lock();
    if (!targetCallback) {
        CLOGE("ICastStreamManagerClient is null");
        return CAST_ENGINE_ERROR;
    }
    mute = targetCallback->GetMute();
    return CAST_ENGINE_SUCCESS;
}

int32_t DlnaPlayController::GetLoopMode(LoopMode &loopMode)
{
    CLOGI("GetLoopMode in");
    std::shared_ptr<ICastStreamManagerClient> targetCallback = callback_.lock();
    loopMode = LoopMode::LOOP_MODE_LIST;
    if (!targetCallback) {
        CLOGE("ICastStreamManagerClient is null");
        return CAST_ENGINE_ERROR;
    }
    loopMode = targetCallback->GetLoopMode();
    CLOGI("GetLoopMode out");
    return CAST_ENGINE_SUCCESS;
}

int32_t DlnaPlayController::GetPlaySpeed(PlaybackSpeed &playbackSpeed)
{
    CLOGI("GetPlaySpeed in");
    std::shared_ptr<ICastStreamManagerClient> targetCallback = callback_.lock();
    playbackSpeed = PlaybackSpeed::SPEED_FORWARD_1_00_X;
    if (!targetCallback) {
        CLOGE("ICastStreamManagerClient is null");
        return CAST_ENGINE_ERROR;
    }
    playbackSpeed = targetCallback->GetPlaySpeed();
    CLOGI("GetPlaySpeed out");
    return CAST_ENGINE_SUCCESS;
}

int32_t DlnaPlayController::GetMediaInfoHolder(MediaInfoHolder &mediaInfoHolder)
{
    CLOGE("Don't support GetMediaInfoHolder");
    return CAST_ENGINE_SUCCESS;
}

int32_t DlnaPlayController::ProvideKeyResponse(const std::string &mediaId, const std::vector<uint8_t> &response)
{
    CLOGE("Don't support ProvideKeyResponse");
    return CAST_ENGINE_ERROR;
}

int32_t DlnaPlayController::Release()
{
    CLOGI("Release in");
    Stop();
    std::lock_guard<std::mutex> lock(sessionCallbackMutex_);
    if (!sessionCallback_) {
        CLOGE("sessionCallback is null");
        return CAST_ENGINE_ERROR;
    }
    sessionCallback_();
    DlnaManager::GetInstance().SetDlnaCallback(nullptr);
    return CAST_ENGINE_SUCCESS;
}

bool DlnaPlayController::DisconnectSession(std::string deviceId)
{
    CLOGI("DisconnectSession in");
    std::shared_ptr<ICastStreamManagerClient> targetCallback = callback_.lock();
    if (!targetCallback) {
        CLOGE("ICastStreamManagerClient is null");
        return false;
    }
    return targetCallback->DisconnectSession(deviceId);
}

void DlnaPlayController::NotifyPlayerError(int errorCode, const std::string &errorMsg)
{
    // DMC_SUCCESS == 0
    if (errorCode == DlnaDmc::DMC_SUCCESS) {
        return;
    }
    CLOGI("DMC errorCode: %{public}d, msg: %{public}s", errorCode, errorMsg.c_str());
    std::lock_guard<std::mutex> lock(listenerMutex_);
    if (playerListener_) {
        // 直接把 DMC 的错误码和原始消息上报给上层
        playerListener_->OnPlayerError(errorCode, errorMsg);
    }
}

std::pair<int, std::string> DlnaPlayController::PlayOnlineMedia(const MediaInfo &mediaInfo)
{
    DlnaUpnpMediaType mediaType;
    if (!GetMediaTypeEnumFromString(mediaInfo.mediaType, mediaType)) {
        CLOGE("Dlna unsupported media type %{public}s", mediaInfo.mediaType.c_str());
        return { ERROR_CODE_INVALID_PARAM, "Dlna unsupported media type" };
    }
    uint32_t instanceId = 0;
    string url = mediaInfo.mediaUrl;
    string meta = DlnaGetMetaData(mediaType, url, mediaInfo);
    dmcApi_.Stop(dlnaDevId_, instanceId);
    auto ret = dmcApi_.SetAvTransportURI(dlnaDevId_, instanceId, url, meta);
    if (ret != DlnaDmc::DMC_SUCCESS) {
        CLOGE("[%{public}s,%{public}d]Dlna set data source failed ", __FUNCTION__, __LINE__);
        return { ret, "Dlna set data source failed" };
    }
    string speed = SpeedToString(currentSpeed_);
    ret = dmcApi_.Play(dlnaDevId_, instanceId, speed);
    CLOGI("Dlna play online return %{public}d", ret);
    return { ret, "Dlna play online return" };
}

std::pair<int, std::string> DlnaPlayController::PlayLocalMedia(const MediaInfo &mediaInfo)
{
    DlnaUpnpMediaType mediaType;
    if (!GetMediaTypeEnumFromString(mediaInfo.mediaType, mediaType)) {
        CLOGE("Dlna unsupported media type: %{public}s", mediaInfo.mediaType.c_str());
        return { ERROR_CODE_INVALID_PARAM, "unsupported media type" };
    }
    uint32_t instanceId = 0;
    dmcApi_.Stop(dlnaDevId_, instanceId);
    string url = "content://" + mediaInfo.mediaType + "/" + mediaInfo.mediaName + mediaSuffixMap[mediaType];
    auto ret = dmcApi_.SetAvTransportURI(dlnaDevId_, instanceId, url, DlnaGetMetaData(mediaType, url, mediaInfo));
    if (ret != DlnaDmc::DMC_SUCCESS) {
        CLOGE("SetAvTransportURI failed: %{public}d", ret);
        return { static_cast<int>(ret), "Dlna set data source failed" };
    }
    string speed = SpeedToString(currentSpeed_);
    ret = dmcApi_.Play(dlnaDevId_, instanceId, speed);
    CLOGI("Dlna play local return %{public}d", ret);
    return { static_cast<int>(ret), "Dlna play local return" };
}

bool DlnaPlayController::GetMediaTypeEnumFromString(string mediaTypeString, DlnaUpnpMediaType& type)
{
    auto item = mediaTypeMap.find(mediaTypeString);
    if (item != mediaTypeMap.end()) {
        type = item->second;
        return true;
    }
    CLOGE("Unsupported mediaType [%{public}s]", mediaTypeString.c_str());
    return false;
}

std::unordered_map<std::string, DlnaPlayController::DlnaUpnpMediaType> DlnaPlayController::mediaTypeMap = {
    {"AUDIO", AUDIO},
    {"VIDEO", VIDEO},
    {"IMAGE", IMAGE}
};

std::unordered_map<DlnaPlayController::DlnaUpnpMediaType, std::string> DlnaPlayController::mediaSuffixMap = {
    {AUDIO, ".mp3"},
    {VIDEO, ".mp4"},
    {IMAGE, ".jpg"}
};

std::unordered_map<PlaybackSpeed, std::string> DlnaPlayController::speedMap = {
    {PlaybackSpeed::SPEED_FORWARD_0_75_X, "0.75"},
    {PlaybackSpeed::SPEED_FORWARD_1_00_X, "1"},
    {PlaybackSpeed::SPEED_FORWARD_1_25_X, "1.25"},
    {PlaybackSpeed::SPEED_FORWARD_1_75_X, "1.75"},
    {PlaybackSpeed::SPEED_FORWARD_2_00_X, "2"}
};

/**
* 获取UpnpClass类型。
*
* @return UpnpClass类型。
*/
string DlnaPlayController::DlnaGetUpnpClass(DlnaUpnpMediaType mediaInfoType)
{
    // 根据元媒体类型或是媒体信息类型构建UpnpClass
    string upnpClass;
    switch (mediaInfoType) {
        case AUDIO:
            upnpClass = "object.item.audioItem.musicTrack";
            break;
        case VIDEO:
            upnpClass = "object.item.videoItem";
            break;
        case IMAGE:
            upnpClass = "object.item.imageItem.photo";
            break;
        case FOLDER:
            upnpClass = "object.container";
            break;
        default:
            break;
    }

    return upnpClass;
}

/**
* 返回DLNA协议要求的媒体元数据。
* 继承的子类必须重写此方法。
*
* @return 描述媒体的MetaData元数据。
*/
string DlnaPlayController::DlnaGetMetaData(DlnaUpnpMediaType mediaInfoType, string url,
    const MediaInfo &mediaInfo)
{
    string title = mediaInfo.mediaName.empty() ? "media" : mediaInfo.mediaName;
    string artist = mediaInfo.mediaArtist.empty() ? "anonymous" : mediaInfo.mediaArtist;
    string size = "0";
    string protocolInfo = "http-get:*:*:*";
    string end = "</DIDL-Lite>";
    string sb;
    sb += "<DIDL-Lite xmlns=\"urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/\" ";
    sb += "xmlns:dlna=\"urn:schemas-dlna-org:metadata-1-0/\" ";
    sb +=  "xmlns:dc=\"http://purl.org/dc/elements/1.1/\" ";
    sb +=  "xmlns:upnp=\"urn:schemas-upnp-org:metadata-1-0/upnp/\">";
    sb.append("<item id=\"0\" parentID=\"0\" restricted=\"1\"><dc:title>");
    sb.append(title);
    sb.append("</dc:title><upnp:artist>");
    sb.append(artist);
    sb.append("</upnp:artist><upnp:class>");
    sb.append(DlnaGetUpnpClass(mediaInfoType));
    sb.append("</upnp:class><res size=\"");
    sb.append(size);
    sb.append("\" protocolInfo=\"");
    sb.append(protocolInfo);
    sb.append("\">");
    sb.append(ReplaceUrl(url));

    sb.append("</res></item>");
    sb.append(end);

    return sb;
}

int32_t DlnaPlayController::SetAvailableCapability(const StreamCapability &streamCapability)
{
    CLOGI("Don't support set streamCapability.");
    static_cast<void>(streamCapability);
    return CAST_ENGINE_ERROR;
}

int32_t DlnaPlayController::GetAvailableCapability(StreamCapability &streamCapability)
{
    CLOGE("Don't support GetAvailableCapability");
    return CAST_ENGINE_ERROR;
}

std::string DlnaPlayController::ReplaceAll(std::string resource_str, std::string sub_str, std::string new_str)
{
    size_t pos = resource_str.find(sub_str); // 查找第一个子串的位置
    while (pos != std::string::npos) {
        resource_str.replace(pos, sub_str.length(), new_str); // 替换子串
        pos = resource_str.find(sub_str, pos + new_str.length()); // 查找下一个子串的位置
    }
    return resource_str;
}

std::string DlnaPlayController::ReplaceUrl(std::string resource_str)
{
    CLOGI("Start Escape URL");
    if (resource_str.find("&amp;") != std::string::npos) {
        return resource_str;
    }
    std::string dst_str = ReplaceAll(resource_str, "&", "&amp;");
    dst_str = ReplaceAll(dst_str, "<", "&lt;");
    dst_str = ReplaceAll(dst_str, ">", "&gt;");
    dst_str = ReplaceAll(dst_str, "\"", "&quot;");
    dst_str = ReplaceAll(dst_str, "'", "&apos;");
    return dst_str;
}

void DlnaPlayController::DlnaCallbackImpl::OnDeviceOffline(unsigned long deviceId)
{
    std::string dlnaDeviceId = DlnaManager::GetInstance().GetDeviceUuid(static_cast<uint32_t>(deviceId));
    CLOGI("OnDeviceDown in, device id:%{public}s", Utils::Mask(dlnaDeviceId).c_str());
    if (dlnaDeviceId.empty()) {
        CLOGW("Unknown dlnaDevId=%{public}lu, ignore.", deviceId);
        return;
    }
    auto controller = controller_.lock();
    if (controller) {
        controller->OnDeviceOffline(deviceId);
    }
}

void DlnaPlayController::OnDeviceOffline(unsigned long deviceId)
{
    std::string dlnaDeviceId = DlnaManager::GetInstance().GetDeviceUuid(static_cast<uint32_t>(deviceId));
    CLOGI("OnDeviceDown in, device id:%{public}s", Utils::Mask(dlnaDeviceId).c_str());
    if (deviceId == dlnaDevId_) {
        if (timer_ != nullptr) {
            timer_->Stop();
        }
        std::shared_ptr<ICastStreamManagerClient> targetCallback = dlnaCallback_.lock();
        if (!targetCallback) {
            CLOGE("ICastStreamManagerClient is null");
            return;
        }
        std::string dev = DlnaManager::GetInstance().GetDeviceUuid(static_cast<uint32_t>(deviceId));
        targetCallback->DisconnectSession(dev);
    }
}

void DlnaPlayController::HandleMessage(const Message &msg)
{
    CLOGI("HandleMessage in");
    MediaInfo mediaInfo;
    json msgJsonMediaInfoJson;
    MessageId msgId = static_cast<MessageId>(msg.what_);
    std::string msgJsonMediaInfo = msg.strArg_;
    switch (msgId) {
        /* 处理播放会话 */
        case MessageId::DLNA_MSG_PLAY:
            CLOGI("Handler start Dlna Play");
            CLOGI("DLNA_MSG_PLAY received raw JSON: %{public}s", msgJsonMediaInfo.c_str());
            msgJsonMediaInfoJson = json::parse(msgJsonMediaInfo);
            CLOGI("DLNA_MSG_PLAY parsed JSON: %{public}s", msgJsonMediaInfoJson.dump().c_str());
            mediaInfo = JsonToMediaInfo(msgJsonMediaInfoJson, mediaInfo);
            PlayInner(mediaInfo);
            break;
        /* 处理暂停会话 */
        case MessageId::DLNA_MSG_PAUSE:
            CLOGI("Handler start Dlna Pause");
            PauseInner();
            break;
        /* 处理停止会话 */
        case MessageId::DLNA_MSG_STOP:
            CLOGI("Handler start Dlna Stop");
            StopInner();
            break;
        default:
            break;
    }
}
} // namespace CastEngineService
} // namespace CastEngine
} // namespace OHOS